import fs from 'fs';
import request from 'request';
import thunkify from 'thunkify';
import mkdirp from 'mkdirp';
import childProcess from 'child_process';

const execSync = childProcess.execSync;
const REQ_TIMEOUT = 300;
const ISO8601_DATE_FORMAT = 'yyyy-MM-dd hh:mm:ss.SSS';

class Kutil {

    * downloadImgSync(uri, dir, fileName) {
        const fileRealDir = `${dir}/${fileName}`;

        function thunkReq(opt, callback) {
            request(opt, (error, res, body) => {
                if (error) {
                    callback(null, 'error');
                } else {
                    callback(null, body);
                }
            });
        }

        const thunkifyReq = thunkify(thunkReq);

        const data = yield thunkifyReq({
            method: 'GET',
            uri,
            timeout: REQ_TIMEOUT,
            encoding: 'binary',
        });

        fs.writeFileSync(fileRealDir, data, 'binary');
    }

    * downloadImgSyncStream(uri, dir, fileName) {
        const fileRealDir = `${dir}/${fileName}`;

        function thunkReq(opt, callback) {
            const writeStream = fs.createWriteStream(opt.path)
                .on('error', () => {
                    callback(null, 'writeStream error');
                })
                .on('finish', () => {
                    callback(null, 'ok');
                });

            request({
                method: 'GET',
                uri: opt.uri,
                timeout: REQ_TIMEOUT,
            })
            .on('error', () => {
                callback(null, 'request error');
            })
            .pipe(writeStream);
        }

        const thunkifyReq = thunkify(thunkReq);

        const isOk = yield thunkifyReq({
            uri,
            path: fileRealDir,
        });

        return isOk;
    }

    * requestJson(url, data) {
        function thunkReq(opt, callback) {
            request(opt, (error, resp, body) => {
                if (error) {
                    callback(null, null);
                } else {
                    callback(null, { status: resp.statusCode, body });
                }
            });
        }

        const thunkifyReq = thunkify(thunkReq);

        const resp = yield thunkifyReq({
            url,
            method: 'POST',
            json: true,
            headers: {
                'content-type': 'application/json',
                'User-Agent': 'GENGHZ',
            },
            body: data,
        });
        return resp;
    }

    * requestGet(url) {
        function thunkReq(opt, callback) {
            request(opt, (error, resp, body) => {
                if (error) {
                    callback(null, null);
                } else {
                    callback(null, { status: resp.statusCode, body });
                }
            });
        }

        const thunkifyReq = thunkify(thunkReq);

        const resp = yield thunkifyReq({
            url,
            method: 'GET',
            headers: {
                'content-type': 'application/json',
                'User-Agent': 'GENGHZ',
            },
        });
        return resp;
    }

    writeFileSync(content, dir, fileName) {
        const fileRealName = `${dir}/${fileName}`;
        mkdirp.sync(dir);
        fs.writeFileSync(fileRealName, content, 'utf8');
    }

    execSync(cmd) {
        execSync(cmd);
    }

    padWithZeros(num, width) {
        let numAsString = `${num}`;
        while (numAsString.length < width) {
            numAsString = `0${numAsString}`;
        }
        return numAsString;
    }

    addZero(num) {
        return this.padWithZeros(num, 2);
    }

    dateOffset(date) {
        // Difference to Greenwich time (GMT) in hours
        const os = Math.abs(date.getTimezoneOffset());
        let h = String(Math.floor(os / 60));
        let m = String(os % 60);
        if (h.length === 1) {
            h = `0${h}`;
        }
        if (m.length === 1) {
            m = `0${m}`;
        }
        return date.getTimezoneOffset() < 0 ? `+${h}${m}` : `-${h}${m}`;
    }

    dateFormat(date, format = ISO8601_DATE_FORMAT) {
        const day = this.addZero(date.getDate());
        const month = this.addZero(date.getMonth() + 1);
        const yearLong = this.addZero(date.getFullYear());
        const yearShort = this.addZero(date.getFullYear().toString().substring(2, 4));
        const year = (format.indexOf('yyyy') > -1 ? yearLong : yearShort);
        const hour = this.addZero(date.getHours());
        const minute = this.addZero(date.getMinutes());
        const second = this.addZero(date.getSeconds());
        const millisecond = this.padWithZeros(date.getMilliseconds(), 3);
        const timeZone = this.dateOffset(date);
        const formatted = format
            .replace(/dd/g, day)
            .replace(/MM/g, month)
            .replace(/y{1,4}/g, year)
            .replace(/hh/g, hour)
            .replace(/mm/g, minute)
            .replace(/ss/g, second)
            .replace(/SSS/g, millisecond)
            .replace(/O/g, timeZone);
        return formatted;
    }

    getNowTimeStr() {
        const d = new Date();
        let res = '';
        res += d.getFullYear();
        res += d.getMonth();
        res += (d.getDate() + 1);
        res += d.getHours();
        res += d.getMinutes();
        res += d.getSeconds();
        return res;
    }

    getTodayZeroSeconds() {
        const d = new Date();
        return new Date(d.getFullYear(), d.getMonth(), d.getDate() + 1).getTime() / 1000;
    }

    getNowSeconds() {
        return Date.parse(new Date()) / 1000;
    }

    formatStr(format, ...args) {
        let i = -1;
        const a = format.replace(/\{\}/g, () => {
            i++;
            return args[i];
        });
        return a;
    }

    getRandomStr(length) {
        const tab = [
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
            'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
            's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
        ];

        let res = '';
        for (let i = 0; i < length; i++) {
            const dex = Math.floor(Math.random() * 36);
            res += tab[dex];
        }
        return res;
    }

    *sleep(ms) {
        function sleepIn({ ts, text = 'awake' }, callback) {
            setTimeout(() => {
                if (callback) {
                    callback(null, text, 'done');
                }
            }, ts);
        }
        const sleepThunk = thunkify(sleepIn);
        yield sleepThunk({ ts: ms });
    }
}

export default Kutil;
